using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.U2D;

[ExecuteAlways]
public class WaterShapeController : MonoBehaviour
{
    [SerializeField] private SpriteShapeController spriteShapeController;

    //prefab
    [SerializeField] private GameObject wavePointPref;

    [SerializeField] private GameObject wavePoints;

    [SerializeField] [Range(0, 100)] private int WavesCount;

    // How stiff should our spring be constnat
    public float springStiffness = 0.1f;

    // Slowing the movement over time
    public float dampening = 0.03f;

    // How much to spread to the other springs
    public float spread = 0.006f;

    //物体在水中游泳的时候产生的水面扰动距离
    //在物周围多远的距离内产生高度扰动
    [SerializeField] private float swimWaveCreateDistance = 0.2f;
    [SerializeField] private float swimWaveEndDistance = 0.1f;

    //高度因子，决定距离产生水体变化影响
    [SerializeField] private float heightFactor;

    private readonly int CorsnersCount = 2;

    private float pointOriginPositionY;

    private Spline spline;
    private List<WaterSpring> springs = new();

    private WaterSwimInDetect waterSwimInDetect;

    private void Awake()
    {
        waterSwimInDetect = transform.parent.GetComponentInChildren<WaterSwimInDetect>();
    }

    private void FixedUpdate()
    {
        JumpInWatetWaveChange();
        SwimWaveChange();
        //波传递
        UpdateSprings();
    }


    //在操作窗口中设置可序列化的属性值时候，会自动调用该函数
    private void OnValidate()
    {
        // Clean waterpoints 
        StartCoroutine(CreateWaves());
    }

    private IEnumerator CreateWaves()
    {
        foreach (Transform child in wavePoints.transform) StartCoroutine(Destroy(child.gameObject));
        yield return null;
        SetWaves();
        yield return null;
    }

    private IEnumerator Destroy(GameObject go)
    {
        yield return null;
        DestroyImmediate(go);
    }

    private void SetWaves()
    {
        var waterSpline = spriteShapeController.spline;
        var waterPointsCount = waterSpline.GetPointCount();

        // Remove middle points for the waves
        // Keep only the corners
        // Removing 1 point at a time we can remove only the 1st point
        // This means every time we remove 1st point the 2nd point becomes first
        for (var i = CorsnersCount; i < waterPointsCount - CorsnersCount; i++) waterSpline.RemovePointAt(CorsnersCount);

        //对于一个矩形来讲，0123，四个点分别是左下，左上，右上和右下
        //一直在2为止插入顶点就是将矩形的12点的中间插入顶点
        var waterTopLeftCorner = waterSpline.GetPosition(1);
        var waterTopRightCorner = waterSpline.GetPosition(2);
        var waterWidth = waterTopRightCorner.x - waterTopLeftCorner.x;

        pointOriginPositionY = waterTopLeftCorner.y;

        var spacingPerWave = waterWidth / (WavesCount + 1);
        // Set new points for the waves
        for (var i = WavesCount; i > 0; i--)
        {
            var index = CorsnersCount;

            var xPosition = waterTopLeftCorner.x + spacingPerWave * i;
            var wavePoint = new Vector3(xPosition, waterTopLeftCorner.y, waterTopLeftCorner.z);
            waterSpline.InsertPointAt(index, wavePoint);
            waterSpline.SetHeight(index, 0.1f);
            waterSpline.SetCorner(index, false);
            waterSpline.SetTangentMode(index, ShapeTangentMode.Continuous);
        }


        // loop through all the wave points
        // plus the both top left and right corners

        springs = new List<WaterSpring>();
        for (var i = 0; i <= WavesCount + 1; i++)
        {
            var index = i + 1;

            Smoothen(waterSpline, index);

            var wavePoint = Instantiate(wavePointPref, wavePoints.transform, false);
            wavePoint.transform.localPosition = waterSpline.GetPosition(index);

            var waterSpring = wavePoint.GetComponent<WaterSpring>();
            waterSpring.Init(spriteShapeController);
            springs.Add(waterSpring);

            // WaveSpring waveSpring = wavePoint.GetComponent<WaveSpring>();
            // waveSpring.Init(spriteShapeController);
        }

        //本地调用
        spline = waterSpline;

        //Splash(5,1f);
    }

    private void Smoothen(Spline waterSpline, int index)
    {
        var position = waterSpline.GetPosition(index);
        var positionPrev = position;
        var positionNext = position;
        if (index > 1) positionPrev = waterSpline.GetPosition(index - 1);
        if (index - 1 <= WavesCount) positionNext = waterSpline.GetPosition(index + 1);

        var forward = gameObject.transform.forward;

        var scale = Mathf.Min((positionNext - position).magnitude, (positionPrev - position).magnitude) * 0.33f;

        var leftTangent = (positionPrev - position).normalized * scale;
        var rightTangent = (positionNext - position).normalized * scale;

        SplineUtility.CalculateTangents(position, positionPrev, positionNext, forward, scale, out rightTangent,
            out leftTangent);

        waterSpline.SetLeftTangent(index, leftTangent);
        waterSpline.SetRightTangent(index, rightTangent);
    }

    private void JumpInWatetWaveChange()
    {
        // 物体跳入水中进行的扰动
        foreach (var waterSpringComponent in springs)
        {
            waterSpringComponent.WaveSpringUpdate(springStiffness, dampening);
            waterSpringComponent.WavePointUpdate();
        }
    }

    private void SwimWaveChange()
    {
        // 游泳前后扰动
        for (var i = 2; i <= WavesCount + 2; ++i)
        {
            var index = i;
            if (waterSwimInDetect.isObjectInWater)
            {
                if (MathF.Abs(waterSwimInDetect.rb.velocity.x) < 0.04f ||
                    MathF.Abs(waterSwimInDetect.rb.velocity.y) > 0.09f) return;
                // 获取物体当前位置和水面点的相对水平距离
                var objectPos = transform.InverseTransformPoint(waterSwimInDetect.transform.position);
                //获取shape坐标并转换到世界坐标
                var pointPos = spline.GetPosition(index);
                // 计算物体与水面点之间的水平距离
                var distance = pointPos.x - objectPos.x;

                //配合跳入水中固有的y值扰动，让在物体游泳的时候别的在波动的地方还可以正常波动
                pointOriginPositionY = pointPos.y;

                // 判断物体水平速度和物体与水面点的距离
                var isTooFar = Mathf.Abs(distance) > swimWaveCreateDistance;
                var isTooClose = Mathf.Abs(distance) < swimWaveEndDistance;
                var isSlow = Mathf.Abs(waterSwimInDetect.rb.velocity.x) < 0.004f;

                // 如果物体离水面点太远或水平速度小于阈值，则恢复到原始高度
                if (isTooFar || isSlow || isTooClose)
                {
                    // 恢复水面点的原始高度
                    pointPos.y = pointOriginPositionY;
                    spline.SetPosition(index, pointPos);
                }
                else
                {
                    // 这里使用物体和水面点之间的距离进行扰动计算
                    var disturbance = (MathF.Cos(distance) + 1) / 2 * heightFactor * waterSwimInDetect.rb.velocity.x *
                                      MathF.Sign(distance);
                    pointPos.y = pointOriginPositionY + disturbance;
                    spline.SetPosition(index, pointPos);
                }
            }
        }
    }


    /// <summary>
    ///     模拟波的传递
    /// </summary>
    //mao 24-11-20 09:46
    private void UpdateSprings()
    {
        var count = springs.Count;
        var left_deltas = new float[count];
        var right_deltas = new float[count];

        for (var i = 0; i < count; i++)
        {
            if (i > 0)
            {
                left_deltas[i] = spread * (springs[i].height - springs[i - 1].height);
                springs[i - 1].velocity += left_deltas[i];
            }

            if (i < springs.Count - 1)
            {
                right_deltas[i] = spread * (springs[i].height - springs[i + 1].height);
                springs[i + 1].velocity += right_deltas[i];
            }
        }
    }

    private void Splash(int index, float speed)
    {
        if (index >= 0 && index < springs.Count) springs[index].velocity += speed;
    }
}